package com.zds.passwordbox.common.util;

import android.util.Base64;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.security.Key;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.security.spec.AlgorithmParameterSpec;

import javax.crypto.Cipher;
import javax.crypto.CipherInputStream;
import javax.crypto.CipherOutputStream;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

public class CryptoUtil {
    private static final String ALGORITHM = "AES";
    /**
     * 加解密算法/工作模式/填充方式
     */
    private static final String AES_CBC_PADDING = "AES/CBC/PKCS5Padding";
    /**
     * 字符编码
     */
    public static final Charset CHARSET_UTF8 = StandardCharsets.UTF_8;

    // 128位密钥，可根据需求调整为192或256位
    private static final int KEY_SIZE = 192;

    /**
     * AES 的 密钥长度，32 字节，范围：16 - 32 字节
     */
    public static final int SECRET_KEY_LENGTH = 32;

    /**
     * 秘钥长度不足 16 个字节时，默认填充位数
     */
    private static final String DEFAULT_VALUE = "0";

    /**
     * 初始化向量,keyIv长度必须等于16
     */
    private static final byte[] keyIv = {
            (byte) 0x12, (byte) 0x34, (byte) 0x33, (byte) 0x33,
            (byte) 0x90, (byte) 0xcd, (byte) 0xcd, (byte) 0xef,
            (byte) 0xfe, (byte) 0xdc, (byte) 0xba, (byte) 0x98,
            (byte) 0x34, (byte) 0x54, (byte) 0x32, (byte) 0x10
    };

    public static SecretKeySpec generateFixedKey(String initStr) {
        try {
            initStr = toMakeKey(initStr);
            // 使用SHA-256对初始字符串进行哈希处理,相同的初始字符串始终会得到相同的哈希值
            MessageDigest digest = MessageDigest.getInstance("SHA-256");
            byte[] hashBytes = digest.digest(initStr.getBytes(CHARSET_UTF8));

            // 截取固定长度（对应密钥长度要求）的字节数组作为密钥材料
            byte[] keyBytes = new byte[KEY_SIZE / 8];
            System.arraycopy(hashBytes, 0, keyBytes, 0, keyBytes.length);

            // 创建并返回AES密钥
            return new SecretKeySpec(keyBytes, ALGORITHM);
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("AES获取密钥出现错误");
        }
    }

    /**
     * 通过这种方式获取随机密钥
     */
    public static String generateInitSecretKey() {
        byte[] key = {
                (byte) 0x12, (byte) 0x34, (byte) 0x33, (byte) 0x33,
                (byte) 0x90, (byte) 0xcd, (byte) 0xcd, (byte) 0xef,
                (byte) 0xfe, (byte) 0xdc, (byte) 0xba, (byte) 0x98,
                (byte) 0x34, (byte) 0x30, (byte) 0x32, (byte) 0x10
        };
        try {
            //获取指定的密钥生成器
            KeyGenerator keyGen = KeyGenerator.getInstance(ALGORITHM);
            //加密强随机数
            SecureRandom secureRandom = new SecureRandom();
            secureRandom.setSeed(key);
            //这里可以是128、192、256、越大越安全
            keyGen.init(KEY_SIZE, secureRandom);
            SecretKey secretKey = keyGen.generateKey();
            // 转String
            return base64Encode(secretKey.getEncoded());
        } catch (NoSuchAlgorithmException e) {
            throw new RuntimeException("AES获取密钥出现错误");
        }
    }

    /**
     * Aes加密(CBC工作模式)
     *
     * @param base64Key 密钥
     * @param data      明文
     * @return Base64编码后的密文
     */
    public static String encrypt(String base64Key, byte[] data)
            throws Exception {
        byte[] key = base64Decode(base64Key);
        //获取SecretKey对象,也可以使用getSecretKey()方法
        Key secretKey = new SecretKeySpec(key, ALGORITHM);
        // 将加密以后的数据进行 Base64 编码
        return encrypt(secretKey, data);
    }

    public static String encrypt(Key secretKey, byte[] data)
            throws Exception {
        //获取指定转换的密码对象Cipher（参数：算法/工作模式/填充模式）
        Cipher cipher = Cipher.getInstance(AES_CBC_PADDING);
        //创建向量参数规范也就是初始化向量
        IvParameterSpec ips = new IvParameterSpec(keyIv);
        //用密钥和一组算法参数规范初始化此Cipher对象（加密模式）
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, ips);
        //执行加密操作
        byte[] encryptByte = cipher.doFinal(data);
        // 将加密以后的数据进行 Base64 编码
        return base64Encode(encryptByte);
    }

    /**
     * Aes加密(ECB工作模式)
     *
     * @param base64Key  密钥
     * @param base64Data Base64编码后的密文
     * @return 明文
     */
    public static String decrypt(String base64Key, String base64Data)
            throws Exception {
        byte[] key = base64Decode(base64Key);
        //获取SecretKey对象,也可以使用getSecretKey()方法
        Key secretKey = new SecretKeySpec(key, ALGORITHM);
        return decrypt(secretKey, base64Data);
    }

    public static String decrypt(Key secretKey, String base64Data)
            throws Exception {
        // 先进行Base64解码
        byte[] data = base64Decode(base64Data);
        //获取指定转换的密码对象Cipher（参数：算法/工作模式/填充模式）
        Cipher cipher = Cipher.getInstance(AES_CBC_PADDING);
        //创建向量参数规范也就是初始化向量
        IvParameterSpec ips = new IvParameterSpec(keyIv);
        //用密钥和一组算法参数规范初始化此Cipher对象（加密模式）
        cipher.init(Cipher.DECRYPT_MODE, secretKey, ips);
        //执行加密操作
        byte[] decryptByte = cipher.doFinal(data);
        return new String(decryptByte, CHARSET_UTF8);
    }

    /**
     * 如果 AES 的密钥小于 {@code length} 的长度，就对秘钥进行补位，保证秘钥安全。
     *
     * @param secretKey 密钥 key
     * @return 密钥
     */
    private static String toMakeKey(String secretKey) {
        // 获取密钥长度
        int strLen = secretKey.length();
        // 判断长度是否小于应有的长度
        if (strLen < CryptoUtil.SECRET_KEY_LENGTH) {
            // 补全位数
            StringBuilder builder = new StringBuilder();
            // 将key添加至builder中
            builder.append(secretKey);
            // 遍历添加默认文本
            for (int i = 0; i < CryptoUtil.SECRET_KEY_LENGTH - strLen; i++) {
                builder.append(CryptoUtil.DEFAULT_VALUE);
            }
            // 赋值
            secretKey = builder.toString();
        }
        return secretKey;
    }

    /**
     * 将 Base64 字符串 解码成 字节数组
     */
    public static byte[] base64Decode(String data) {
        return Base64.decode(data, Base64.NO_WRAP);
    }

    /**
     * 将 字节数组 转换成 Base64 编码
     */
    public static String base64Encode(byte[] data) {
        return Base64.encodeToString(data, Base64.NO_WRAP);
    }

    /**
     * 对文件进行AES加密
     *
     * @param sourceFile 待加密文件
     * @param dir        加密后的文件存储路径
     * @param toFileName 加密后的文件名称
     * @param secretKey  密钥
     * @return 加密后的文件
     */
    public static File encryptFile(File sourceFile, String dir, String toFileName, String secretKey) {
        try {
            // 创建加密后的文件
            File encryptFile = new File(dir, toFileName);
            // 根据文件创建输出流
            FileOutputStream outputStream = new FileOutputStream(encryptFile);
            // 初始化 Cipher
            Cipher cipher = initFileAESCipher(secretKey, Cipher.ENCRYPT_MODE);
            // 以加密流写入文件
            CipherInputStream cipherInputStream = new CipherInputStream(
                    new FileInputStream(sourceFile), cipher);
            // 创建缓存字节数组
            byte[] buffer = new byte[1024 * 2];
            // 读取
            int len;
            // 读取加密并写入文件
            while ((len = cipherInputStream.read(buffer)) != -1) {
                outputStream.write(buffer, 0, len);
                outputStream.flush();
            }
            // 关闭加密输入流
            cipherInputStream.close();
            closeStream(outputStream);
            return encryptFile;
        } catch (Exception e) {
            throw new RuntimeException("文件加密出错，e" + e.getMessage());
        }
    }

    /**
     * AES解密文件
     *
     * @param sourceFile 源加密文件
     * @param dir        解密后的文件存储路径
     * @param toFileName 解密后的文件名称
     * @param secretKey  密钥
     */
    public static File decryptFile(File sourceFile, String dir, String toFileName, String secretKey) {
        try {
            // 创建解密文件
            File decryptFile = new File(dir, toFileName);
            // 初始化Cipher
            Cipher cipher = initFileAESCipher(secretKey, Cipher.DECRYPT_MODE);
            // 根据源文件创建输入流
            FileInputStream inputStream = new FileInputStream(sourceFile);
            // 获取解密输出流
            CipherOutputStream cipherOutputStream = new CipherOutputStream(
                    new FileOutputStream(decryptFile), cipher);
            // 创建缓冲字节数组
            byte[] buffer = new byte[1024 * 2];
            int len;
            // 读取解密并写入
            while ((len = inputStream.read(buffer)) >= 0) {
                cipherOutputStream.write(buffer, 0, len);
                cipherOutputStream.flush();
            }
            // 关闭流
            cipherOutputStream.close();
            closeStream(inputStream);
            return decryptFile;
        } catch (IOException e) {
            throw new RuntimeException("文件解密出错，e" + e.getMessage());
        }
    }

    /**
     * 初始化 AES Cipher
     *
     * @param secretKey  密钥
     * @param cipherMode 加密模式
     * @return 密钥
     */
    private static Cipher initFileAESCipher(String secretKey, int cipherMode) {
        try {
            // 创建密钥规格
            SecretKeySpec secretKeySpec = generateFixedKey(secretKey);
            // 获取密钥
            Cipher cipher = Cipher.getInstance(AES_CBC_PADDING);
            // 初始化
            cipher.init(cipherMode, secretKeySpec, new IvParameterSpec(new byte[cipher.getBlockSize()]));
            return cipher;
        } catch (Exception e) {

            throw new RuntimeException("获取文件加密对象出错，e" + e.getMessage());
        }
    }

    /**
     * 关闭流
     *
     * @param closeable 实现Closeable接口
     */
    private static void closeStream(Closeable closeable) {
        try {
            if (closeable != null) closeable.close();
        } catch (Exception ignored) {

        }
    }

    // 生成随机的初始向量（IV）
    private static byte[] generateInitializationVector() {
        byte[] iv = new byte[16];
        new SecureRandom().nextBytes(iv);
        return iv;
    }

    //// 生成AES密钥
    //private static SecretKey generateAESKey() throws NoSuchAlgorithmException {
    //    KeyGenerator keyGenerator = KeyGenerator.getInstance(ALGORITHM);
    //    keyGenerator.init(KEY_SIZE);
    //    return keyGenerator.generateKey();
    //}
    // 加密文件方法
    public static void encryptStream(byte[] input, OutputStream os, SecretKey secretKey) {
        byte[] iv = generateInitializationVector();
        AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
        try {
            ByteArrayInputStream is = new ByteArrayInputStream(input);
            Cipher cipher = Cipher.getInstance(AES_CBC_PADDING);
            cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec);
            os.write(iv); // 先将初始向量写入输出文件

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = is.read(buffer))!= -1) {
                byte[] encryptedBytes = cipher.update(buffer, 0, bytesRead);
                if (encryptedBytes!= null) {
                    os.write(encryptedBytes);
                }
            }
            byte[] finalEncryptedBytes = cipher.doFinal();
            if (finalEncryptedBytes!= null) {
                os.write(finalEncryptedBytes);
            }
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    // 解密文件方法
    public static byte[] decryptFile(InputStream is, SecretKey secretKey) {
        try (ByteArrayOutputStream os = new ByteArrayOutputStream()) {
            byte[] iv = new byte[16];
            if (is.read(iv)!= 16) {
               throw new RuntimeException("文件iv获取失败!可能文件已损坏!");
            }
            AlgorithmParameterSpec paramSpec = new IvParameterSpec(iv);
            Cipher cipher = Cipher.getInstance(AES_CBC_PADDING);
            cipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec);

            byte[] buffer = new byte[1024];
            int bytesRead;
            while ((bytesRead = is.read(buffer))!= -1) {
                byte[] decryptedBytes = cipher.update(buffer, 0, bytesRead);
                if (decryptedBytes!= null) {
                    os.write(decryptedBytes);
                }
            }
            byte[] finalDecryptedBytes = cipher.doFinal();
            if (finalDecryptedBytes!= null) {
                os.write(finalDecryptedBytes);
            }
            return os.toByteArray();
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }
    }

    // 将密钥转换为Base64字符串，方便存储或传输（示例简单使用，实际可能需要更安全的存储方式）
    public static String keyToString(SecretKey secretKey) {
        return Base64.encodeToString(secretKey.getEncoded(), Base64.DEFAULT);
    }

    // 从Base64字符串恢复密钥
    public static SecretKey stringToKey(String keyBase64) {
        byte[] keyBytes = Base64.decode(keyBase64, Base64.DEFAULT);
        return new SecretKeySpec(keyBytes, ALGORITHM);
    }


}
